package com.tepth.appmanageservice.controllers;

import android.os.Environment;
import android.util.Log;

import com.tepth.appmanageservice.communicate.interfaces.IInterthreaded;
import com.tepth.appmanageservice.core.ClientInfo;
import com.tepth.appmanageservice.core.StaticClass;
import com.tepth.appmanageservice.enums.EnumEvent;
import com.tepth.appmanageservice.interfaces.IAction;
import com.tepth.appmanageservice.protocol.Controller;
import com.tepth.appmanageservice.protocol.bodys.CommonReply;
import com.tepth.appmanageservice.protocol.bodys.aes.AesReply;
import com.tepth.appmanageservice.protocol.bodys.login.ClientLoginReply;
import com.tepth.appmanageservice.protocol.bodys.ordinaryfile.OperationOrdinaryFile;
import com.tepth.appmanageservice.protocol.bodys.ordinaryfile.OperationOrdinaryFileReply;
import com.tepth.appmanageservice.protocol.bodys.ordinaryfile.OrdinaryFileIssued;
import com.tepth.appmanageservice.protocol.bodys.ordinaryfile.OrdinaryFileRequestReply;
import com.tepth.appmanageservice.protocol.bodys.ordinaryfile.RunFileResult;
import com.tepth.appmanageservice.protocol.bodys.ordinaryfile.ViewOrdinaryFileList;
import com.tepth.appmanageservice.protocol.bodys.ordinaryfile.ViewOrdinaryFileListReply;
import com.tepth.appmanageservice.protocol.bodys.system.ModifyDomainPort;
import com.tepth.appmanageservice.protocol.bodys.system.ModifyHeartbeatFrequency;
import com.tepth.appmanageservice.protocol.bodys.system.PublicKeyRequestReply;
import com.tepth.appmanageservice.protocol.interfaces.IProtocolEvent;
import com.tepth.appmanageservice.protocol.packets.Packets;
import com.tepth.appmanageservice.tools.LogTool;
import com.tepth.appmanageservice.tools.ext.ExtIO;
import com.tepth.appmanageservice.tools.ext.ExtPro;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;

public class Client implements IProtocolEvent {
    private BigInteger N = null;

    public BigInteger getN() {
        return this.N;
    }

    private IInterthreaded interthreaded;
    private IAction<EnumEvent> onAESReplyEvent;
    private byte[] AES_KEYS;

    public byte[] getAES_KEYS() {
        return this.AES_KEYS;
    }

    public Client(IInterthreaded interthreaded) {
        this.interthreaded = interthreaded;
    }

    @Override
    public void Invoke(Packets packets) {
        LogTool.i("接收", packets.toString());
        try {
            switch (packets.getHeader().getId()) {
                case CommonReply.ID:
                    this.commonReplyed(packets);
                    break;
                // Login ---------------------------------------------------------------------------------
                case ClientLoginReply.ID:
                    this.clientLoginReply(packets);
                    break;
                // System --------------------------------------------------------------------------------
                case 0x0E13:
                    this.viewDomainPort(packets);
                    break;
                case ModifyDomainPort.ID:
                    this.modifyDomainPort(packets);
                    break;
                case 0x0E04:
                    this.viewHeartbeatFrequency(packets);
                    break;
                case ModifyHeartbeatFrequency.ID:
                    this.modifyHeartbeatFrequency(packets);
                    break;
                case 0x0E02:
                    this.viewSystemVersionNumber(packets);
                    break;
                // Aes -----------------------------------------------------------------------------------
                case AesReply.ID:
                    this.aesReply(packets);
                    break;
                case PublicKeyRequestReply.ID:
                    this.publicKeyRequestReply(packets);
                    break;
                // Ordinary File -------------------------------------------------------------------------
                case OrdinaryFileIssued.ID: // ��ͨ�ļ��·�
                    this.ordinaryFileIssued(packets);
                    break;
                case OrdinaryFileRequestReply.ID: // ��ͨ�ļ�����Ӧ��
                    this.ordinaryFileRequestReply(packets);
                    break;
                case ViewOrdinaryFileList.ID: // Զ�̲鿴�ն���ͨ�ļ��б�
                    this.viewOrdinaryFileList(packets);
                    break;
                case OperationOrdinaryFile.ID: // Զ�̲������·����ն˵��ļ�
                    this.operationOrdinaryFile(packets);
                    break;
                // Sd ------------------------------------------------------------------------------------
                case 0x0E10:
                    this.viewSdStatus(packets);
                    break;
                case 0x0E11:
                    this.viewSdSize(packets);
                    break;
                case 0x0E12:
                    this.viewSdUsedSize(packets);
                    break;

                default:
                    this.defaultBreak(packets);
                    break;
            }
        } catch (Exception e) {
            try {
                if (packets.getHeader() != null) {
                    Packets data = Controller.getCommonReply(packets, (byte) 2);
                    try {
                        interthreaded.send(data);
                    } catch (Exception e2) {
                        Log.e("io", "ExceptionCommonReply发送失败。");
                    }
                } else {
                    Packets data = Controller.getCommonReply((byte) 2);
                    try {
                        interthreaded.send(data);
                    } catch (Exception e2) {
                        Log.e("io", "ExceptionCommonReply发送失败。");
                    }
                }
            } catch (Exception e2) {
                Log.e("io", "Exception发送失败。");
            }
        }
    }

    private void defaultBreak(Packets packets) {
        Packets data = Controller.getCommonReply(packets, (byte) 0);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "modifyDomainPort发送失败。");
        }
    }

    private void commonReplyed(Packets packets) {
        CommonReply body = (CommonReply) packets.getBody();
        Boolean isOk = isCommonReplyConfirm(body);
        Log.e("通用应答", String.valueOf(body.getReply_id()) + (isOk ? "成功" : "失败"));
    }

    // Login ---------------------------------------------------------------------------------------------

    private void clientLoginReply(Packets packets) {
        ClientLoginReply body = (ClientLoginReply) packets.getBody();
        interthreaded.setLogin(body.getValue() == 0);
    }

    // System --------------------------------------------------------------------------------------------

    private void viewDomainPort(Packets packets) {
        short port = (short) StaticClass.get_config().getServerPort();
        String domain = StaticClass.get_config().getServerDomain();
        Packets data = Controller.getViewDomainPortReply(packets, domain, port);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "viewDomainPort发送失败");
        }
    }

    private void modifyDomainPort(Packets packets) {
        ModifyDomainPort body = packets.getBodyModel();
        StaticClass.get_config().setServerPort(body.getPort());
        StaticClass.get_config().setServerDomain(body.getDomain());

        Packets data = Controller.getCommonReply(packets, (byte) 0);

        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "modifyDomainPort发送失败");
        }
    }

    private void viewHeartbeatFrequency(Packets packets) {
        int value = StaticClass.get_config().getHeartRate();
        Packets data = Controller.getViewHeartbeatFrequencyReply(packets, value);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "viewHeartbeatFrequency发送失败");
        }
    }

    private void modifyHeartbeatFrequency(Packets packets) {
        ModifyHeartbeatFrequency modifyHeartbeatFrequency = packets.getBodyModel();
        StaticClass.get_config().setHeartRate(modifyHeartbeatFrequency.getHeartbeatFrequency());
        Packets data = Controller.getCommonReply(packets, (byte) 0);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "modifyHeartbeatFrequency发送失败");
        }
    }

    private void viewSystemVersionNumber(Packets packets) {
        Packets data = Controller.getViewSystemVersionNumberReply(packets, ClientInfo.SYSTEM_VERSION_NUMBER);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "viewSystemVersionNumber发送失败");
        }
    }

    // Aes -----------------------------------------------------------------------------------------------

    private void aesReply(Packets packets) throws Exception {
        AesReply body = packets.getBodyModel();
        if (body.getValue() != 0)
            throw new Exception("动态密钥注册失败");

        if (this.onAESReplyEvent != null)
            this.onAESReplyEvent.Invoke(EnumEvent.PublicKeyEvent);
    }

    private void publicKeyRequestReply(Packets packets) throws Exception {
        PublicKeyRequestReply body = packets.getBodyModel();
        if (body.getValue() != 0)
            throw new Exception("公钥请求失败");

        this.N = body.getKeyN();
        this.AES_KEYS = ExtPro.AesRandomKeys();

        byte[] keys = ExtPro.encrypt(this.AES_KEYS, body.getKeyE(), this.N);
        Packets data = Controller.getAesKey(keys);

        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "AES动态密钥发送失败");
        }
    }

    // Ordinary File -------------------------------------------------------------------------------------

    private void ordinaryFileIssued(Packets packets) {
        Log.i("ordinaryFileIssued", String.valueOf(packets.getHeader().getPacketSequenceNumber()));
        Packets data = Controller.getCommonReply(packets, (byte) 0);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "ordinaryFileIssued应答发送失败。");
        }

        if (!packets.getHeader().isSubcontractor()) {
            Log.i("OrdinaryFileRequest", "普通文件请求");
            try {
                this.ordinaryFileRequest((OrdinaryFileIssued) packets.getBody());
            } catch (Exception e) {
                Log.e("io", "OrdinaryFileRequest发送失败。");
            }
        }
    }

    private void ordinaryFileRequest(final OrdinaryFileIssued body) {
        Thread tr = new Thread() {
            @Override
            public void run() {
                try {
                    short packetCount = body.getPacketCount();
                    for (short i = 1; i <= packetCount; i++) {
                        Packets requestData = Controller.getOrdinaryFileRequest(body.getMd5(), body.getFileType(), i);
                        interthreaded.send(requestData);

                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (IOException e) {
                    Log.e("io", "OrdinaryFileRequest发送失败");
                }
            }
        };
        tr.start();
    }

    private void ordinaryFileRequestReply(Packets packets) {
        Log.i("ordinaryFileRReply", String.valueOf(packets.getHeader().getPacketSequenceNumber()));
    }

    private void viewOrdinaryFileList(Packets packets) {
        Log.i("viewOrdinaryFileList", String.valueOf(packets.getHeader().getPacketSequenceNumber()));

        ViewOrdinaryFileList body = (ViewOrdinaryFileList) packets.getBody();

        File file = new File(ExtIO.getFileDir(""));
        File[] tempList = file.listFiles();
        ViewOrdinaryFileListReply.OrdinaryFile[] ordinaryFileList = new ViewOrdinaryFileListReply.OrdinaryFile[tempList.length];

        for (int i = 0; i < ordinaryFileList.length; i++) {
            com.tepth.appmanageservice.tools.Ref<Integer> ref = new com.tepth.appmanageservice.tools.Ref<>();
            String md5 = "";
            try {
                md5 = ExtIO.getMd5ByFile(tempList[i], ref);
            } catch (FileNotFoundException ignored) {

            }

            ordinaryFileList[i] = new ViewOrdinaryFileListReply.OrdinaryFile(tempList[i].getName(), (byte) (ref.get() / 1024), md5);
        }

        Packets sedData = Controller.getViewOrdinaryFileListReply(packets, ordinaryFileList, body.getFileType());

        try {
            interthreaded.send(sedData);
        } catch (IOException e) {
            Log.e("io", "ViewOrdinaryFileListReply发送失败");
        }
    }

    private void operationOrdinaryFile(Packets packets) {
        Log.i("operationOrdinaryFile", String.valueOf(packets.getHeader().getPacketSequenceNumber()));

        OperationOrdinaryFile body = (OperationOrdinaryFile) packets.getBody();

        byte isSupport = (byte) 1;

        OperationOrdinaryFileReply replyBody = new OperationOrdinaryFileReply();
        replyBody.setReplySerialNumber(packets.getHeader().getSerialNumber1());
        replyBody.setIsSupport(isSupport);

        Packets replyData = new Packets(replyBody);
        try {
            interthreaded.send(replyData);
        } catch (IOException e) {
            Log.e("io", "OperationOrdinaryFileReply发送失败");
        }

        // 启动文件成功
        byte runResult = (byte) 1;

        RunFileResult runBody = new RunFileResult();
        runBody.setFileType(body.getFileType());
        runBody.setMd5(body.getMd5());
        runBody.setRunResult(runResult);

        Packets runData = new Packets(runBody);
        try {
            interthreaded.send(runData);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Sd ------------------------------------------------------------------------------------------------

    private void viewSdStatus(Packets packets) {
        byte value = (byte)(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ? 1 : 0);
        Packets data = Controller.getViewSdStatusReply(packets, value);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "viewSDStatus发送失败");
        }
    }

    private void viewSdSize(Packets packets) {
        int sdSize = 4000;
        Packets data = Controller.getViewSdSizeReply(packets, sdSize);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "viewSDSize发送失败");
        }
    }

    private void viewSdUsedSize(Packets packets) {
        int sdSize = 1200;
        Packets data = Controller.getViewSdUsedSizeReply(packets, sdSize);
        try {
            interthreaded.send(data);
        } catch (IOException e) {
            Log.e("io", "viewSDUsedSize发送失败");
        }
    }

    // ---------------------------------------------------------------------------------------------------

    private Boolean isCommonReplyConfirm(CommonReply commonReply) {
        return commonReply.getValue() == 0;
    }

    public void setOnAESReplyEvent(IAction<EnumEvent> action) {
        this.onAESReplyEvent = action;
    }
}